home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 November: Tool Chest / Dev.CD Nov 00 TC Disk 2.toast / pc / sample code / processes / launchwithdoc2 / launchwithdoc2.c next >
Encoding:
C/C++ Source or Header  |  2000-09-28  |  16.2 KB  |  528 lines

  1. /*
  2.     File:        LaunchWithDoc2.c
  3.  
  4.     Contains:    Document-launching sample program
  5.                  Loosely based on C.K. Haun's LaunchWithDoc
  6.     
  7.                 This snippet includes these useful routines:
  8.     
  9.                  OpenSpecifiedDocument
  10.                     finds the creator application for a document, whether or not
  11.                     the app is running, launches the app if necessary, and sends
  12.                     the Apple Event necessary to get the app to open the document
  13.     
  14.                 FindApplicationFromDocument
  15.                     searches the mounted volumes for the application which
  16.                      created a document
  17.     
  18.                 LaunchApplicationWithDocument
  19.                     launches an application which is not running and passes it
  20.                     the OpenDocuments event as part of the launch parameters
  21.     
  22.                 SendOpenDocumentEventToProcess
  23.                      sends an OpenDocuments Apple event to a running program
  24.     
  25.                 BuildOpenDocumentsEvent
  26.                     utility function to build an 'odoc' event from a list of 
  27.                      FSSpecs.
  28.     
  29.                  Remember that a target application need not be Apple event aware
  30.                 in order for the OpenDocuments event to succeed (the System will
  31.                 pull "puppet strings", simulating the events necessary to make the
  32.                 target app open the document)
  33.     
  34.                 However, LaunchWithDoc must be high level event aware (as set in the
  35.                 SIZE resource) in order to send Apple events using AESend
  36.     
  37.     Written by: Greg Robbins
  38.                 modified by Nitin Ganatra    
  39.  
  40.     Copyright:    Copyright © 1993-1999 by Apple Computer, Inc., All Rights Reserved.
  41.  
  42.                 You may incorporate this Apple sample source code into your program(s) without
  43.                 restriction. This Apple sample source code has been provided "AS IS" and the
  44.                 responsibility for its operation is yours. You are not permitted to redistribute
  45.                 this Apple sample source code as "Apple sample source code" after having made
  46.                 changes. If you're going to re-distribute the source, we require that you make
  47.                 it clear in the source that the code was descended from Apple sample source
  48.                 code, but that you've made changes.
  49.  
  50.     Change History (most recent first):
  51.                 7/27/1999    Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
  52.                 
  53.  
  54. */
  55.  
  56. #include <QuickDraw.h>
  57. #include <StandardFile.h>
  58. #include <Fonts.h>
  59. #include <Menus.h>
  60. #include <Dialogs.h>
  61. #include <Events.h>
  62. #include <Files.h>
  63. #include <TextEdit.h>
  64. #include <Memory.h>
  65. #include <Errors.h>
  66. #include <Processes.h>
  67. #include <AppleEvents.h>
  68. #include <Aliases.h>
  69.  
  70. // prototypes
  71.  
  72. OSErr OpenSpecifiedDocument(const FSSpec * documentFSSpecPtr);
  73. OSErr FindApplicationFromDocument(const FSSpec * documentFSSpecPtr,
  74.     FSSpecPtr applicationFSSpecPtr);
  75. void ReportError(StringPtr messageStr);
  76.  
  77. // what functions changed since LaunchWithDoc2
  78.  
  79. OSErr BuildOpenDocumentEvent(ProcessSerialNumber *targetPSN, 
  80.             const FSSpec *theSpecArray, const short numOfSpecs, AppleEvent *odocAppleEvent);
  81. OSErr SendOpenDocumentEventToProcess(ProcessSerialNumber *targetPSN,
  82.             const FSSpec *theSpecArray, const short numOfSpecs);
  83. OSErr LaunchApplicationWithDocument(const FSSpec *applicationFSSpecPtr,
  84.             const FSSpec *theSpecArray, const short numOfSpecs);
  85.  
  86.  
  87. // main program
  88. //
  89. // the main routine raises a std file dialog to let the
  90. // user choose a document and then opens the document
  91. // in the appropriate application
  92.  
  93. void main(void)
  94. {
  95.     OSErr retCode;
  96.     StandardFileReply documentStdFileReply;
  97.     SFTypeList mySFTypeList;
  98.     
  99.     // initialize the toolbox
  100.     InitGraf(&qd.thePort); InitFonts(); InitWindows(); InitMenus();
  101.     TEInit(); InitDialogs(nil); InitCursor();
  102.     
  103.     // Simplest case: get a document and open it
  104.     StandardGetFile(nil, -1, mySFTypeList, &documentStdFileReply);
  105.     if (documentStdFileReply.sfGood) {
  106.         
  107.         retCode = OpenSpecifiedDocument(&documentStdFileReply.sfFile);
  108.         if (retCode != noErr) ReportError("\p OpenDocument failed");
  109.     }
  110.  
  111. /*    
  112.     // Another case: get an application to launch and three documents
  113.     // to open on startup
  114.     StandardGetFile(nil, -1, mySFTypeList, &documentStdFileReply);
  115.     if (documentStdFileReply.sfGood) {
  116.  
  117.         appSpec = documentStdFileReply.sfFile;
  118.         for (index = 0; index < 3; index ++) {
  119.             StandardGetFile(nil, -1, mySFTypeList, &documentStdFileReply);
  120.             if (documentStdFileReply.sfGood)
  121.                 docTmpSpec[index] = documentStdFileReply.sfFile;
  122.  
  123.         }
  124.         
  125.         retCode = LaunchApplicationWithDocument(&appSpec, docTmpSpec, index);
  126.     }
  127. */
  128.  
  129. }
  130.  
  131. void ReportError(StringPtr messageStr)
  132. {
  133.     DebugStr(messageStr);
  134. }
  135.  
  136.  
  137. // OpenSpecifiedDocument searches to see if the application which
  138. // created the document is already running.  If so, it sends
  139. // an OpenSpecifiedDocuments Apple event to the target application
  140. // (remember that, because of puppet strings, this works even
  141. // if the target application is not Apple event-aware.)
  142.  
  143. OSErr OpenSpecifiedDocument(const FSSpec * documentFSSpecPtr)
  144. {
  145.     OSErr retCode;
  146.     ProcessSerialNumber currPSN;
  147.     ProcessInfoRec currProcessInfo;
  148.     FSSpec applicationSpec;
  149.     FInfo documentFInfo;
  150.     Boolean foundRunningProcessFlag;
  151.     
  152.     // verify the document file exists and get its creator type
  153.     
  154.     retCode = FSpGetFInfo(documentFSSpecPtr, &documentFInfo);
  155.     if (retCode != noErr) goto Bail;
  156.     
  157.     // check the current processes to see if the creator app is already
  158.     // running, and get its process serial number (as currPSN)
  159.     
  160.     currPSN.lowLongOfPSN = kNoProcess;
  161.     currPSN.highLongOfPSN = 0;
  162.     
  163.     currProcessInfo.processInfoLength = sizeof(ProcessInfoRec);
  164.     currProcessInfo.processName = nil;
  165.     currProcessInfo.processAppSpec = &applicationSpec;
  166.     
  167.     foundRunningProcessFlag = false;
  168.     while (GetNextProcess(&currPSN) == noErr) {
  169.         if (GetProcessInformation(&currPSN, &currProcessInfo) == noErr) {
  170.             if (currProcessInfo.processSignature == documentFInfo.fdCreator) {
  171.                 foundRunningProcessFlag = true;
  172.                 break;
  173.             }
  174.         }
  175.     }
  176.     
  177.     // if the creator is running, send it an OpenDocuments Apple event
  178.     // since there is no need to launch it
  179.     
  180.     if (foundRunningProcessFlag)
  181.         retCode = SendOpenDocumentEventToProcess(&currPSN, documentFSSpecPtr, 1);
  182.     
  183.     // else if the creator is not running, find it on disk and launch
  184.     // it with the OpenDocuments event included as a part of the
  185.     // launch parameters
  186.     
  187.     else {
  188.         retCode = FindApplicationFromDocument(documentFSSpecPtr, &applicationSpec);
  189.         
  190.         if (retCode == noErr)
  191.         
  192.             retCode = LaunchApplicationWithDocument(&applicationSpec,
  193.                 documentFSSpecPtr, 1);
  194.     }
  195.     
  196. Bail:
  197.     return retCode;
  198. }
  199.  
  200.  
  201. //----------------------------------------------------------------------------
  202. // LaunchApplicationWithDocument
  203. //
  204. // given an application and any number of documents, 
  205. // LaunchApplicationWithDocument launches the application and passes the 
  206. // application an OpenDocuments event for the document(s)
  207. //----------------------------------------------------------------------------
  208. OSErr LaunchApplicationWithDocument(
  209.     const FSSpec        *applicationFSSpecPtr,
  210.     const FSSpec         *theSpecArray,
  211.     const short            numOfSpecs)
  212. {
  213.     OSErr retCode;
  214.     LaunchParamBlockRec launchParams;
  215.     AppleEvent theAppleEvent;
  216.     AEDesc launchParamDesc;
  217.     ProcessSerialNumber targetPSN;
  218.     
  219.     // to simplify cleanup, ensure that handles are nil to start
  220.     launchParams.launchAppParameters    = nil;
  221.     theAppleEvent.dataHandle            = nil;
  222.     launchParamDesc.dataHandle            = nil;
  223.     
  224.     if (theSpecArray != nil) {
  225.  
  226.         // because 'odoc' events require a address descriptor, I just 
  227.         // grab the PSN for the current process.  It doesn't matter what
  228.         // it is, because it's never used.
  229.         (void) GetCurrentProcess(&targetPSN);
  230.         
  231.         // build an 'odoc' event given the array of FSSpecs and the ProcessSerialNumber
  232.         retCode = BuildOpenDocumentEvent(&targetPSN, theSpecArray, numOfSpecs, &theAppleEvent);
  233.         
  234.         if (retCode == noErr) {
  235.         
  236.             // coerce the AppleEvent into app parameters, for _LaunchApplication
  237.             retCode = AECoerceDesc(&theAppleEvent, typeAppParameters, &launchParamDesc);
  238.             if (retCode != noErr) goto Bail;
  239.             
  240.             // fill in the launch parameter block, including the
  241.             // Apple event, and make the launch call
  242.             HLock((Handle) launchParamDesc.dataHandle);
  243.             launchParams.launchAppParameters =
  244.                 (AppParametersPtr) *(launchParamDesc.dataHandle);
  245.  
  246.         }
  247.  
  248.     }
  249.  
  250.     launchParams.launchBlockID        = extendedBlock;
  251.     launchParams.launchEPBLength    = extendedBlockLen;
  252.     launchParams.launchFileFlags    = launchNoFileFlags;
  253.     launchParams.launchControlFlags    = launchContinue;
  254.     launchParams.launchAppSpec        = (FSSpecPtr)applicationFSSpecPtr;
  255.  
  256.     retCode = LaunchApplication(&launchParams);
  257.  
  258. Bail:
  259.     // dispose of everything that was allocated
  260.     if (theAppleEvent.dataHandle != nil)     (void) AEDisposeDesc(&theAppleEvent);
  261.     if (launchParamDesc.dataHandle != nil)   (void) AEDisposeDesc(&launchParamDesc);
  262.     
  263.     return retCode;
  264.  
  265. }
  266.  
  267.  
  268. //----------------------------------------------------------------------------
  269. // SendOpenDocumentEventToProcess
  270. //
  271. // given an application's serial number and any number of documents, 
  272. // SendOpenDocumentEventToProcess passes 
  273. // the application an OpenDocuments event for the document
  274. //----------------------------------------------------------------------------
  275. OSErr SendOpenDocumentEventToProcess(
  276.     ProcessSerialNumber            *targetPSN,
  277.     const FSSpec                 *theSpecArray,
  278.     const short                    numOfSpecs)
  279. {
  280.     OSErr retCode;
  281.     AppleEvent theAppleEvent, theReplyEvent;
  282.  
  283.     theAppleEvent.dataHandle = nil;
  284.     retCode = BuildOpenDocumentEvent(targetPSN, theSpecArray, numOfSpecs, &theAppleEvent);
  285.  
  286.     if (retCode == noErr)
  287.         retCode = AESend(&theAppleEvent,
  288.                         &theReplyEvent, 
  289.                         kAENoReply, 
  290.                         kAENormalPriority,
  291.                         kAEDefaultTimeout,
  292.                         nil,
  293.                         nil);
  294.     
  295.     // dispose of the AppleEvent if it was allocated    
  296.     if (theAppleEvent.dataHandle != nil)  
  297.         (void) AEDisposeDesc(&theAppleEvent);
  298.     
  299.     return retCode;
  300.  
  301. }
  302.  
  303.  
  304. // FindApplicationFromDocument uses the Desktop Database to
  305. // locate the creator application for the given document
  306. //
  307. // this routine will first check the desktop database of the disk
  308. // containing the document, then the desktop database of all local
  309. // disks, then the desktop databases of all server volumes
  310. // (so up to three passes will be made)
  311.  
  312. OSErr FindApplicationFromDocument(const FSSpec * documentFSSpecPtr,
  313.     FSSpecPtr applicationFSSpecPtr)
  314. {
  315.     enum { documentPass, localPass, remotePass, donePass } volumePass;
  316.     DTPBRec desktopParams;
  317.     HParamBlockRec hfsParams;
  318.     FInfo documentFInfo;
  319.     short volumeIndex;
  320.     Boolean foundFlag;
  321.     GetVolParmsInfoBuffer volumeInfoBuffer;
  322.     OSErr retCode;
  323.     
  324. // dkj 12/94 initialize flag to false (thanks to Peter Baral for pointing out this bug)
  325.     foundFlag = false;
  326.  
  327.     // verify the document file exists and get its creator type
  328.     
  329.     retCode = FSpGetFInfo(documentFSSpecPtr, &documentFInfo);
  330.     if (retCode != noErr) goto Bail;
  331.     
  332.     volumePass = documentPass;
  333.     volumeIndex = 0;
  334.     
  335.     do {
  336.         
  337.         // first, find the vRefNum of the volume whose Desktop Database
  338.         // we're checking this time
  339.         
  340.         // if we're on the initial pass (documentPass) just use
  341.         // the vRefNum of the document itself
  342.         
  343.         if (volumePass == documentPass)
  344.         
  345.             desktopParams.ioVRefNum = documentFSSpecPtr->vRefNum;
  346.         
  347.         // otherwise, find the vRefNum of the next volume appropriate
  348.         // for this pass
  349.         
  350.         else {
  351.             
  352.             volumeIndex++;
  353.             
  354.             // convert the volumeIndex into a vRefNum
  355.             
  356.             hfsParams.volumeParam.ioNamePtr = nil;
  357.             hfsParams.volumeParam.ioVRefNum = 0;
  358.             hfsParams.volumeParam.ioVolIndex = volumeIndex;
  359.             retCode = PBHGetVInfoSync(&hfsParams);
  360.             
  361.             // a nsvErr indicates that the current pass is over
  362.             if (retCode == nsvErr) goto SkipThisVolume;
  363.             if (retCode != noErr) goto Bail;
  364.             
  365.             // since we handled the document volume during the documentPass,
  366.             // skip it if we have hit that volume again
  367.             
  368.             if (hfsParams.volumeParam.ioVRefNum == documentFSSpecPtr->vRefNum)
  369.                 goto SkipThisVolume;
  370.             
  371.             // call GetVolParms to determine if this volume is a server
  372.             // (a remote volume)
  373.             
  374.             hfsParams.ioParam.ioBuffer = (Ptr) &volumeInfoBuffer;
  375.             hfsParams.ioParam.ioReqCount = sizeof(GetVolParmsInfoBuffer);
  376.             retCode = PBHGetVolParmsSync(&hfsParams);
  377.             if (retCode != noErr) goto Bail;
  378.             
  379.             // if the vMServerAdr field of the volume information buffer
  380.             // is zero, this is a local volume; skip this volume
  381.             // if it's local on a remote pass or remote on a local pass
  382.             
  383.             if ((volumeInfoBuffer.vMServerAdr != 0) !=
  384.                 (volumePass == remotePass)) goto SkipThisVolume;
  385.             
  386.             // okay, now we've found the vRefNum for our desktop database call
  387.             
  388.             desktopParams.ioVRefNum = hfsParams.volumeParam.ioVRefNum;
  389.         }
  390.         
  391.         // find the path refNum for the desktop database for
  392.         // the volume we're interested in
  393.         
  394.         desktopParams.ioNamePtr = nil;
  395.         
  396.         retCode = PBDTGetPath(&desktopParams);
  397.         if (retCode == noErr && desktopParams.ioDTRefNum != 0) {
  398.         
  399.             // use the GetAPPL call to find the preferred application
  400.             // for opening any document with this one's creator
  401.             
  402.             desktopParams.ioIndex = 0;
  403.             desktopParams.ioFileCreator = documentFInfo.fdCreator;
  404.             desktopParams.ioNamePtr = applicationFSSpecPtr->name;
  405.             retCode = PBDTGetAPPLSync(&desktopParams);
  406.             
  407.             if (retCode == noErr) {
  408.             
  409.                 // okay, found it; fill in the application file spec
  410.                 // and set the flag indicating we're done
  411.                 
  412.                 applicationFSSpecPtr->parID = desktopParams.ioAPPLParID;
  413.                 applicationFSSpecPtr->vRefNum = desktopParams.ioVRefNum;
  414.                 foundFlag = true;
  415.                 
  416.             }
  417.         }
  418.         
  419.     SkipThisVolume:
  420.     
  421.         // if retCode indicates a no such volume error or if this
  422.         // was the first pass, it's time to move on to the next pass
  423.         
  424.         if (retCode == nsvErr || volumePass == documentPass) {
  425.             volumePass++;
  426.             volumeIndex = 0;
  427.         }
  428.         
  429.     } while (foundFlag == false && volumePass != donePass);
  430.     
  431. Bail:
  432.     return retCode;
  433. }
  434.  
  435.  
  436. //----------------------------------------------------------------------------
  437. // BuildOpenDocumentsEvent
  438. //
  439. // General utility to turn a ProcessSerialNumber and a list of FSSpecs into
  440. // an 'odoc' AppleEvent with the ProcessSerialNumber as the target
  441. // application.  Used by SendOpenDocumentEventToProcess, and
  442. // LaunchApplicationWithDocument.
  443. //----------------------------------------------------------------------------
  444. OSErr BuildOpenDocumentEvent(
  445.     ProcessSerialNumber        *targetPSN, 
  446.     const FSSpec             *theSpecArray, 
  447.     const short                numOfSpecs,
  448.     AppleEvent                *odocAppleEvent)
  449. {
  450.     OSErr            retCode;
  451.     AppleEvent        theAppleEvent;
  452.     AEDesc            targetAddrDesc, docDesc;
  453.     AEDescList        docDescList;
  454.     AliasHandle        docAlias;
  455.     short            counter;
  456.     FSSpecPtr        specIterator;
  457.  
  458.     // to simplify cleanup, ensure that handles are nil to start
  459.     targetAddrDesc.dataHandle    = nil;
  460.     theAppleEvent.dataHandle    = nil;
  461.     docDescList.dataHandle        = nil;
  462.     docDesc.dataHandle            = nil;
  463.     docAlias                    = nil;
  464.  
  465.     // create an address descriptor based on the serial number of
  466.     // the target process
  467.     retCode = AECreateDesc(typeProcessSerialNumber, (Ptr) targetPSN,
  468.         sizeof(ProcessSerialNumber), &targetAddrDesc);
  469.     if (retCode != noErr) goto Bail;
  470.     
  471.     // make a descriptor list containing just a descriptor with an
  472.     // alias to the document
  473.     retCode = AECreateList(nil, 0, false, &docDescList);
  474.     if (retCode != noErr) goto Bail;
  475.  
  476.     // start at the beginning of the FSSpec list, and start adding
  477.     // them to the document list descriptor
  478.  
  479.     // NOTE: we need to make sure we dispose of the aliases and the
  480.     // AE descriptor in this loop, otherwise there will be memory
  481.     // leaks.
  482.     specIterator = (FSSpecPtr)theSpecArray;
  483.     for (counter = 0; counter < numOfSpecs; counter ++) {    
  484.  
  485.         retCode = NewAlias(nil, &specIterator[counter], &docAlias);
  486.         if (retCode != noErr) goto Bail;
  487.         
  488.         HLock((Handle) docAlias);
  489.         retCode = AECreateDesc(typeAlias, (Ptr) *docAlias, 
  490.             InlineGetHandleSize((Handle) docAlias), &docDesc);
  491.         HUnlock((Handle) docAlias);
  492.         
  493.         if (retCode != noErr) goto Bail;
  494.         // the alias is now in the AEDescriptor, so dispose of it
  495.         DisposeHandle((Handle)docAlias); docAlias = nil;
  496.         
  497.         retCode = AEPutDesc(&docDescList, 0, &docDesc);
  498.         if (retCode != noErr) goto Bail;
  499.  
  500.         // the alias is now in the AE document list, so dispose of it
  501.         retCode = AEDisposeDesc(&docDesc);
  502.         if (retCode != noErr) goto Bail;
  503.  
  504.     }
  505.     
  506.     // now make the 'odoc' AppleEvent descriptor and insert the 
  507.     // document descriptor list as the direct object
  508.     retCode = AECreateAppleEvent(kCoreEventClass, kAEOpenDocuments,
  509.         &targetAddrDesc, kAutoGenerateReturnID, kAnyTransactionID,
  510.         &theAppleEvent);
  511.     if (retCode != noErr) goto Bail;
  512.     
  513.     retCode = AEPutParamDesc(&theAppleEvent, keyDirectObject, &docDescList);
  514.     if (retCode != noErr) goto Bail;
  515.     
  516.     *odocAppleEvent = theAppleEvent;
  517.  
  518. Bail:
  519.     // dispose of everything that was allocated, except the return AE
  520.     if (targetAddrDesc.dataHandle != nil)  (void) AEDisposeDesc(&targetAddrDesc);
  521.     if (docDescList.dataHandle != nil)  (void) AEDisposeDesc(&docDescList);
  522.     if (docDesc.dataHandle != nil)  (void) AEDisposeDesc(&docDesc);
  523.     if (docAlias != nil)  DisposeHandle((Handle) docAlias);
  524.     
  525.     return retCode;
  526.  
  527. }
  528.